package org.planx.xmlstore.routing.messaging;

import java.io.*;
import java.math.*;
import java.net.*;
import java.util.*;
import junit.framework.*;
import org.planx.xmlstore.routing.*;
import org.planx.xmlstore.routing.operation.*;

/**
 * Tests the messaging framework. Multiple concurrent threads accessing the
 * MessageServer is not tested, this is tested in the general Kademlia test.
 **/
public class MessageServerTest extends TestCase {
    private MessageServer server1, server2;
    private Node local1, local2;
    private StringBuffer out;

    public MessageServerTest(String s) {
        super(s);
    }

    public static Test suite() {
        return new TestSuite(MessageServerTest.class);
    }

    public static void main(String args[]) {
        junit.textui.TestRunner.run(MessageServerTest.class);
    }

    protected void setUp() {
        try {
            out = new StringBuffer();

            local1 = new Node(InetAddress.getLocalHost(), 3333,
                                Identifier.randomIdentifier());
            Space space1 = new Space(local1, new Configuration());
            MessageFactory factory1 = new MessageFactoryImpl(null, local1, space1);
            server1 = new MessageServer(3333, factory1, 500);

            local2 = new Node(InetAddress.getLocalHost(), 4444,
                                Identifier.randomIdentifier());
            Space space2 = new Space(local2, new Configuration());
            MessageFactory factory2 = new MessageFactoryImpl(null, local2, space2);
            server2 = new MessageServer(4444, factory2, 500);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void tearDown() {
        server1.close();
        server2.close();
    }

    /**
     * Tests that the MessageServer and Receiver can send, receive, and reply messages.
     **/
    public synchronized void testCommunication() throws Exception {
        Message lookup = new NodeLookupMessage(local1, Identifier.randomIdentifier());
        int comm = server1.send(lookup, local2.getInetAddress(),
                                local2.getPort(), new Client(this));
        wait();
        String expected = "receive comm="+comm+" message=NodeReplyMessage"+
                          "[origin="+local2+",nodes=["+local1+", "+local2+"]]";
        assertEquals(expected, out.toString());
    }

    /**
     * Tests timeout.
     **/
    public synchronized void testTimeout() throws Exception {
        Message lookup = new NodeLookupMessage(local1, Identifier.randomIdentifier());
        int comm = server1.send(lookup, local2.getInetAddress(),
                                5555, new Client(this)); // wrong port
        wait();
        String expected = "timeout comm="+comm;
        assertEquals(expected, out.toString());
    }

    class Client implements Receiver {
        private Object o;

        public Client(Object o) {
            this.o = o;
        }

        public void receive(Message m, int comm) throws IOException {
            NodeReplyMessage reply = (NodeReplyMessage) m;
            // Sort nodes so that the order is deterministic
            Collections.sort(reply.getNodes(), new Node.DistanceComparator(local1.getId()));
            out.append("receive comm="+comm+" message="+m);
            synchronized (o) {
                o.notify();
            }
        }

        public void timeout(int comm) throws IOException {
            out.append("timeout comm="+comm);
            synchronized (o) {
                o.notify();
            }
        }
    }
}
